/*
 * Copyright 2013 JROSE
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jrose.core.mongo;

import com.mongodb.Mongo;
import com.mongodb.MongoOptions;
import com.mongodb.ServerAddress;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.jrose.api.config.ConfigManager;
import org.jrose.api.config.MongoConfig;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.mongodb.core.MongoFactoryBean;
import org.springframework.data.mongodb.core.MongoOptionsFactoryBean;

import java.util.ArrayList;
import java.util.List;

/**
 * {@code FactoryBean} implementation for building {@code MongoConnection} instance.
 *
 * @author Xiaohai Zhang
 * @since 2013-09-03 12:54
 */
@NoArgsConstructor
public class MongoConnectionFactoryBean implements FactoryBean<MongoConnection>, InitializingBean, DisposableBean {
    @Setter private ConfigManager configManager;

    private MongoConnection mongoConnection;

    @Override
    public void afterPropertiesSet() throws Exception {
        Validate.notNull(configManager, "'configManager' no defined");

        MongoConfig mongoConfig = configManager.getMongoConfig();
        Validate.notNull(mongoConfig, "'mongoConfig' not found in configManager");

        Mongo mongo = buildMongo(mongoConfig);
        mongoConnection = new MongoConnection(mongo);
    }

    @Override
    public void destroy() throws Exception {
        if (mongoConnection != null) {
            mongoConnection.close();
        }
    }

    @Override
    public MongoConnection getObject() throws Exception {
        return mongoConnection;
    }

    @Override
    public Class<?> getObjectType() {
        return MongoConnection.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    // private methods:

    private static Mongo buildMongo(MongoConfig mongoConfig) throws Exception {
        List<ServerAddress> serverAddressList = new ArrayList<>();
        String[] replicaSetSegments = StringUtils.split(mongoConfig.getReplicaSet(), ",");
        for (String replicaSetSegment : replicaSetSegments) {
            if (StringUtils.isBlank(replicaSetSegment)) {
                continue;
            }
            replicaSetSegment = replicaSetSegment.trim();
            if (StringUtils.contains(replicaSetSegment, ":")) {
                String host = StringUtils.substringBefore(replicaSetSegment, ":");
                int port = Integer.parseInt(StringUtils.substringAfter(replicaSetSegment, ":"));
                ServerAddress serverAddress = new ServerAddress(host, port);
                serverAddressList.add(serverAddress);
            } else {
                @SuppressWarnings("UnnecessaryLocalVariable")
                String host = replicaSetSegment;
                int port = 27017;
                ServerAddress serverAddress = new ServerAddress(host, port);
                serverAddressList.add(serverAddress);
            }
        }

        MongoOptionsFactoryBean mongoOptionsFactoryBean = new MongoOptionsFactoryBean();
        mongoOptionsFactoryBean.setAutoConnectRetry(mongoConfig.isAutoConnectRetry());
        mongoOptionsFactoryBean.setConnectionsPerHost(mongoConfig.getConnectionsPerHost());
        mongoOptionsFactoryBean.setConnectTimeout(mongoConfig.getConnectTimeout());
        mongoOptionsFactoryBean.setMaxAutoConnectRetryTime(mongoConfig.getMaxAutoConnectRetryTime());
        mongoOptionsFactoryBean.setMaxWaitTime(mongoConfig.getMaxWaitTime());
        mongoOptionsFactoryBean.setSlaveOk(mongoConfig.isSlaveOk());
        mongoOptionsFactoryBean.setSocketKeepAlive(mongoConfig.isSocketKeepAlive());
        mongoOptionsFactoryBean.setSocketTimeout(mongoConfig.getSocketTimeout());
        mongoOptionsFactoryBean.setThreadsAllowedToBlockForConnectionMultiplier(mongoConfig.getThreadsAllowedToBlockForConnectionMultiplier());
        mongoOptionsFactoryBean.setWriteFsync(mongoConfig.isWriteFsync());
        mongoOptionsFactoryBean.setWriteNumber(mongoConfig.getWriteNumber());
        mongoOptionsFactoryBean.setWriteTimeout(mongoConfig.getWriteTimeout());
        mongoOptionsFactoryBean.afterPropertiesSet();
        MongoOptions options = mongoOptionsFactoryBean.getObject();

        MongoFactoryBean mongoFactoryBean = new MongoFactoryBean();
        mongoFactoryBean.setMongoOptions(options);
        mongoFactoryBean.setReplicaSetSeeds(serverAddressList.toArray(new ServerAddress[serverAddressList.size()]));
        mongoFactoryBean.afterPropertiesSet();

        return mongoFactoryBean.getObject();
    }
}
